using System;
using System.Collections.Generic;
using System.Linq;
using Db4objects.Db4o.Linq;
using SharedObjects;

namespace TheGameDatabaseTool
{
	public partial class DatabaseMainScript
	{
		#region fields
		private readonly List<Cpu> _createdCpUs = new List<Cpu>();
		private readonly List<MemoryType> _createdMemory = new List<MemoryType>();
		private readonly List<NetworkAdapter> _createdNiCs = new List<NetworkAdapter>();

		#region Hardware Balancing
		//please refer to the included HardwareDifficultyMapping.xlsx for how these functions came to be.
		//The excel sheet calculates various scenarios of hardware combinations and their effect on player power
		//If you want to modify these values, use the Excel sheet first to see what effect this will have.
		private readonly Func<int, int> _cpuGetCpuIndex = i => (i-1) * 2 + 1;
		private readonly Func<int, int> _cpuGetMemIndex = i => (int) Math.Ceiling(i*0.5);
		private readonly Func<int, int> _cpuGetNicIndex = i => (int) Math.Ceiling(i*0.5) - 1;
		private readonly Func<int, int> _cpuGetNumCpuSockets = i => i*2;

		private readonly Func<int, int> _memGetCpuIndex = i => (int) Math.Ceiling(i*0.5);
		private readonly Func<int, int> _memGetMemIndex = i => i-1;
		private readonly Func<int, int> _memGetNicIndex = i => (int)Math.Ceiling(i*0.5) - 1;
		private readonly Func<int, int> _memGetNumMemSockets = i => i < 5 ? i*2 + 4 : (i)*2;

		private readonly Func<int, int> _nicGetCpuIndex = i => (int) Math.Ceiling(i*0.5);
		private readonly Func<int, int> _nicGetMemIndex = i => (int) Math.Ceiling(i*0.5);
		private readonly Func<int, int> _nicGetNicIndex = i => i - 1;
		private readonly Func<int, int> _nicGetNumNicSockets = i => (int) Math.Ceiling(i*0.5)*2;

		private readonly Func<int, int> _hpGetCpuIndex = i => (i - 1)*2 + 1;
		private readonly Func<int, int> _hpGetMemIndex = i => i - 1;
		private readonly Func<int, int> _hpGetNicIndex = i => i - 1;
		private readonly Func<int, int> _hpGetNumCpuSockets = i => i*2;
		private readonly Func<int, int> _hpGetNumMemSockets = i => i < 5 ? i*2 + 4 : (i)*2;
		private readonly Func<int, int> _hpGetNumNicSockets = i => (int) Math.Ceiling(i*0.5)*2;
		#endregion

		private readonly Func<int, int, int> _getMinimumDifficulty = (i,j) => (int) Math.Ceiling((i + 1)/(j/4.0));
		#endregion

		private void CreateHardware()
		{
			CreateCpUs();
			CreateMemoryTypes();
			CreateNetworkAdapters();

			Guid id = new Guid("8FB3D250-AC0F-49CB-B80D-F9DB5454CDF7");
			string name = "Default Gateway";
			GatewayBlueprint bp = CreateBlueprint(id.ToLong(), name, 1, 2, 1, 1.0, 32, 1.0, 1.0, 32, 1.0, 0, 0);
			bp.IsDefault = true;
			Db.PersistEntity(bp);

			CreateBlueprintsWithCpuFocus();
			CreateBlueprintsWithMemoryFocus();
			CreateBlueprintsWithBandwidthFocus();
			CreateHighPerformanceBlueprints();
		}

		private void CreateCpUs()
		{
			double speedBase = 1.0;
			double speedFactor = 1.5;
			double priceBase = 1000;
			double priceFactor = 1.25;

			Dictionary<Guid, string> cpuNames = new Dictionary<Guid, string>
			{
				{new Guid("5D630056-4D02-4F70-AA07-5F729D5D6C67"), "Standard Single Core CPU {0:###.#} GHz"},
				{new Guid("C4F2538A-AB6D-4BC5-9CB8-9219F204A0E4"), "Standard Single Core CPU {0:###.#} GHz"},
				{new Guid("9A0194E0-82C4-447D-B16D-6AF2CE172A68"), "Standard Single Core CPU {0:###.#} GHz"},
				{new Guid("DFAA5CD3-EA0F-4D89-99E1-B2D7FCE2B87D"), "Standard Single Core CPU {0:###.#} GHz"},
				{new Guid("6B6E17B4-78C8-49FC-B3E8-06626DAF0B94"), "Advanced Dual Core CPU {0:###.#} GHz"},
				{new Guid("5E7829A7-0DB9-44C2-88DD-2E38096AB1F9"), "Advanced Dual Core CPU {0:###.#} GHz"},
				{new Guid("0F4E80C5-EDDC-4BEF-B5EA-7E0B62EFD6FD"), "Advanced Dual Core CPU {0:###.#} GHz"},
				{new Guid("A017194D-27FC-4EED-AABC-56C111B1BD5C"), "Advanced Dual Core CPU {0:###.#} GHz"},
				{new Guid("5E549D84-2DD5-4382-BDDB-E53718E400C5"), "Standard Quad Core CPU {0:###.#} GHz"},
				{new Guid("3DB27FA1-97C7-4325-B7BC-13613547F3E6"), "Standard Quad Core CPU {0:###.#} GHz"},
				{new Guid("B5974FEC-E59E-4A0F-A5FF-DF7483BECA2E"), "Standard Quad Core CPU {0:###.#} GHz"},
				{new Guid("474BC2D2-7697-4504-A054-4EFBD4438E4A"), "Standard Quad Core CPU {0:###.#} GHz"},
				{new Guid("208D307E-2B33-4C6B-9C9F-2DAE5B0B6505"), "Professional Octa Core CPU {0:###.#} GHz"},
				{new Guid("98DCC987-F0BC-4A22-9E65-725A18C429E7"), "Professional Octa Core CPU {0:###.#} GHz"},
				{new Guid("AD613D44-0685-411C-A78D-A01F4C9FF1AC"), "Massively Parallel Super Computing CPU {0:###.#} GHz"},
				{new Guid("8E1FE5B1-FF2E-4123-9E46-0DCBD0CD2267"), "Massively Parallel Super Computing CPU {0:###.#} GHz"}
			};

			for (int i = 0; i < 16; i++)
			{
				int minDiff = _getMinimumDifficulty(i, cpuNames.Count);// (int)Math.Ceiling((i + 1) / (cpuNames.Count / 4.0));
				Guid id = cpuNames.ElementAt(i).Key;
				string name = cpuNames[id];

				Cpu cpu = (from Cpu x in Db where x.Id == id.ToLong() select x).FirstOrDefault();
				if (cpu == null)
				{
					cpu = new Cpu();
					cpu.Id = id.ToLong();
				}
				cpu.BasePrice = (int)(priceBase * Math.Pow(i + 1, priceFactor));
				cpu.IsDefault = i == 0 ? true : false;
				cpu.IsDeleted = false;
				cpu.MinimumMarketDifficulty = minDiff;
				cpu.Modifier = speedBase * Math.Pow(i + 1, speedFactor);
				cpu.Name = string.Format(name, cpu.Modifier);
				Db.PersistEntity(cpu);
				_createdCpUs.Add(cpu);
			}
		}

		private void CreateMemoryTypes()
		{
			// for calculations and definitions of these values, refer to HardwareDifficultyMapping.xlsx
			int capacityBase = 16;
			double priceBase = 1500;
			double priceFactor = 1.24565;

			Dictionary<Guid, string> memoryNames = new Dictionary<Guid, string>
			{
				{new Guid("6EEC9876-71B8-44D1-A482-30C631202FBE"), "Standard Memory {0:###} GB"},
				{new Guid("BE14C987-18B0-4A19-97FC-23F99736A428"), "Standard Memory {0:###} GB"},
				{new Guid("6645D927-BDF7-48F0-8BD3-E481380BA152"), "Advanced Memory {0:###} GB"},
				{new Guid("BBCAF387-AA4C-403A-BF1D-47804B01BB6D"), "Advanced Memory {0:###} GB"},
				{new Guid("ADE9D60F-043B-4312-956A-BBC458A1FBF3"), "Professional Memory {0:###} GB"},
				{new Guid("CACD1FCA-DA2A-4934-802B-CFEBB429A616"), "Professional Memory {0:###} GB"},
				{new Guid("CAE8E9B2-A975-488F-8481-45802C6706DF"), "High Capacity Memory {0:###} GB"},
				{new Guid("8522AA2F-D857-499B-B6BD-723E066CADFA"), "High Capacity Memory {0:###} GB"},
			};

			for (int i = 0; i < 8; i++)
			{
				int minDiff = _getMinimumDifficulty(i, memoryNames.Count);
				Guid id = memoryNames.ElementAt(i).Key;
				string name = memoryNames[id];
				MemoryType mem = (from MemoryType mt in Db where mt.Id == id.ToLong() select mt).FirstOrDefault();
				if (mem == null)
				{
					mem = new MemoryType();
					mem.Id = id.ToLong();
				}

				mem.BasePrice = (int)Math.Ceiling(priceBase * Math.Pow(i + 1, priceFactor));
				mem.Modifier = (i + 1) * capacityBase;
				mem.IsDefault = i == 1 ? true : false;
				mem.IsDeleted = false;
				mem.MinimumMarketDifficulty = minDiff;
				mem.Name = string.Format(name, mem.Modifier);
				Db.PersistEntity(mem);
				_createdMemory.Add(mem);
			}
		}

		private void CreateNetworkAdapters()
		{
			double speedBase = 1.0;
			double speedFactor = 2.32195;
			double priceBase = 500;
			double priceFactor = 1.25;

			Dictionary<Guid, string> nicNames = new Dictionary<Guid, string>
			{
				{new Guid("DCF6C8AA-6421-4CDC-8401-A180AC6EF1C1"), "Standard Network Adapter {0:###.#} GBit"},
				{new Guid("A3FFCCC6-AAF4-4787-A332-6D5F49C5F2A8"), "Standard Network Adapter {0:###.#} GBit"},
				{new Guid("0D438CCC-A925-4BAD-90BA-73A4D2CAF89E"), "Advanced Network Adapter {0:###.#} GBit"},
				{new Guid("35732920-E3FC-48CB-8F7A-941B4D2FCB88"), "Advanced Network Adapter {0:###.#} GBit"},
				{new Guid("4A6BFA3D-40AA-4BA4-A378-62058E4357E8"), "Professional Network Adapter {0:###.#} GBit"},
				{new Guid("98DB25B3-8041-4F02-B6FB-F6D9644BCBAC"), "Professional Network Adapter {0:###.#} GBit"},
				{new Guid("A61D7B5A-2009-4C9B-B8B2-4F067C540358"), "High Speed Network Adapter {0:###.#} GBit"},
				{new Guid("2BF18AD4-C8CE-4530-9C33-E9B77EDE6308"), "High Speed Network Adapter {0:###.#} GBit"}
			};

			for (int i = 0; i < 8; i++)
			{
				int minDiff = _getMinimumDifficulty(i, nicNames.Count);
				Guid id = nicNames.ElementAt(i).Key;
				string name = nicNames[id];

				NetworkAdapter nic = (from NetworkAdapter nwa in Db where nwa.Id == id.ToLong() select nwa).FirstOrDefault();
				if (nic == null)
				{
					nic = new NetworkAdapter();
					nic.Id = id.ToLong();
				}
				nic.BasePrice = (int)Math.Ceiling(priceBase * Math.Pow(i + 1, priceFactor));
				nic.IsDefault = i == 0 ? true : false;
				nic.IsDeleted = false;
				nic.MinimumMarketDifficulty = minDiff;
				nic.Modifier = speedBase * Math.Pow(i + 1, speedFactor);
				nic.Name = string.Format(name, nic.Modifier);
				Db.PersistEntity(nic);
				_createdNiCs.Add(nic);
			}
		}

		private void CreateBlueprintsWithCpuFocus()
		{
			double priceBase = 499.0;
			Dictionary<Guid, string> names = new Dictionary<Guid, string>
			{
				{new Guid("8381A19B-8D46-4CD5-9432-BC9C45E416F3"), "Standard Number Cruncher Mk. I"}, 
				{new Guid("A9735DF2-1D53-43F2-9E7B-80AC843D4C04"), "Standard Number Cruncher Mk. II"},
				{new Guid("78358048-0DE2-4136-B52E-439A1AEF83EC"), "Standard Number Cruncher Mk. III"},
				{new Guid("F9C9BC28-284A-43AD-8326-7CDB54DC8031"), "Standard Number Cruncher Mk. IV"},
				{new Guid("F88DB7CB-D7BC-4C68-A6F9-3E3D947DC9B7"), "Professional Number Cruncher Mk. I"},
				{new Guid("22C5F632-FC83-49F5-B79C-5CA4B21CD1C0"), "Professional Number Cruncher Mk. II"},
				{new Guid("2723E768-3009-4776-AED0-F380AA0E0EA3"), "Professional Number Cruncher Mk. III"},
				{new Guid("DD7E7898-F911-44EC-B471-928F72C5D4E7"), "Professional Number Cruncher Mk. IV"},
			};

			for(int i = 1; i < 9; i++)
			{
				Guid id = names.ElementAt(i-1).Key;
				string name = names[id];

				int numCpuSockets = _cpuGetNumCpuSockets(i);
				int cpuIndex = _cpuGetCpuIndex(i);
				int numMemSockets = 2;
				int numNicSockets = 1;

				double maxAllowedCpuSpeed = Math.Ceiling(_createdCpUs[cpuIndex].Modifier);
				double maxTotalCpuSpeed = numCpuSockets*maxAllowedCpuSpeed;
				
				int maxAllowedMemSize = (int)_createdMemory[_cpuGetMemIndex(i)].Modifier;
				int maxTotalMemory = numMemSockets*maxAllowedMemSize;

				double maxAllowedNicSpeed = Math.Ceiling(_createdNiCs[_cpuGetNicIndex(i)].Modifier);
				double maxTotalNicSpeed = numNicSockets*maxAllowedNicSpeed;
				int minDiff = (int)Math.Ceiling(i*0.5);
				GatewayBlueprint g = CreateBlueprint(id.ToLong(), name, numCpuSockets, numMemSockets, numNicSockets, maxTotalCpuSpeed, maxTotalMemory, maxTotalNicSpeed, maxAllowedCpuSpeed, maxAllowedMemSize, maxAllowedNicSpeed, priceBase, minDiff);
				Db.PersistEntity(g);
			}
		}

		private void CreateBlueprintsWithMemoryFocus()
		{
			double priceBase = 499.0;
			Dictionary<Guid, string> names = new Dictionary<Guid, string>
			{
				{new Guid("86A0FB60-44DB-471B-B39E-B83A7DF16E06"), "Standard File Server Mk. I"},
				{new Guid("0FC655DB-BB35-4C3E-A6C2-134297746FF1"), "Standard File Server Mk. II"},
				{new Guid("773617BD-F654-4189-9E56-89E932ECCDAF"), "Standard File Server Mk. III"},
				{new Guid("88E28344-388F-4969-8F48-22769170C61F"), "Standard File Server Mk. IV"},
				{new Guid("AA8AF2F1-F73C-4751-91CE-2B4FF78BAC4F"), "High Capacity File Server Mk. I"},
				{new Guid("16264FA7-BB75-4549-8ABF-CC67CB6DF44A"), "High Capacity File Server Mk. II"},
				{new Guid("8F0B1048-CF43-4F60-821E-BBEEA3ADF847"), "High Capacity File Server Mk. III"},
				{new Guid("C73A6AFE-9BCA-4093-9583-1C29DF0999DD"), "High Capacity File Server Mk. IV"},
			};

			for (int i = 1; i < 9; i++)
			{
				Guid id = names.ElementAt(i-1).Key;
				string name = names[id];

				int numCpuSockets = 1;
				int numNicSockets = 1;
				int numMemSockets = _memGetNumMemSockets(i);

				int maxAllowedMemSize = (int)_createdMemory[_memGetMemIndex(i)].Modifier;
				int maxTotalMemory = numMemSockets * maxAllowedMemSize;

				double maxAllowedCpuSpeed = Math.Ceiling(_createdCpUs[_memGetCpuIndex(i)].Modifier);
				double maxTotalCpuSpeed = numCpuSockets * maxAllowedCpuSpeed;

				double maxAllowedNicSpeed = Math.Ceiling(_createdNiCs[_memGetNicIndex(i)].Modifier);
				double maxTotalNicSpeed = numNicSockets * maxAllowedNicSpeed;

				int minDiff = (int)Math.Ceiling(i * 0.5);
				GatewayBlueprint g = CreateBlueprint(id.ToLong(), name, numCpuSockets, numMemSockets, numNicSockets, maxTotalCpuSpeed, maxTotalMemory, maxTotalNicSpeed, maxAllowedCpuSpeed, maxAllowedMemSize, maxAllowedNicSpeed, priceBase, minDiff);
				Db.PersistEntity(g);
			}
		}

		private void CreateBlueprintsWithBandwidthFocus()
		{
			double priceBase = 499.0;
			Dictionary<Guid, string> names = new Dictionary<Guid, string>
			{
				{new Guid("24A487A6-2B73-4D37-9420-09FAEFC8482A"), "Standard Backbone Box Mk. I"},
				{new Guid("442EBCA2-B862-4F07-A363-BB281D834757"), "Standard Backbone Box Mk. II"},
				{new Guid("487E76A9-81DD-49E8-A2CD-823941FE6F28"), "Standard Backbone Box Mk. III"},
				{new Guid("6F72EB81-00BA-496B-81A5-E6EFB1C3F00B"), "Standard Backbone Box Mk. IV"},
				{new Guid("A4756736-6072-4E27-B84A-EDB933B71F16"), "High Throughput Backbone Box Mk. I"},
				{new Guid("06FC14E2-3C5C-4576-8B82-82506DE737BD"), "High Throughput Backbone Box Mk. II"},
				{new Guid("75DB0A11-9936-4E1B-AB86-857525A67C59"), "High Throughput Backbone Box Mk. III"},
				{new Guid("654C782D-0A10-4128-8D80-E9769A98136A"), "High Throughput Backbone Box Mk. IV"}
			};

			for(int i = 1; i < 9; i++)
			{
				Guid id = names.ElementAt(i-1).Key;
				string name = names[id];

				int numNicSockets = _nicGetNumNicSockets(i);
				int numCpuSockets = 1;
				int numMemSockets = 2;

				int maxAllowedMemSize = (int)_createdMemory[_nicGetMemIndex(i)].Modifier;
				int maxTotalMemory = numMemSockets*maxAllowedMemSize;

				double maxAllowedCpuSpeed = Math.Ceiling(_createdCpUs[_nicGetCpuIndex(i)].Modifier);
				double maxTotalCpuSpeed = numCpuSockets * maxAllowedCpuSpeed;

				double maxAllowedNicSpeed = Math.Ceiling(_createdNiCs[_nicGetNicIndex(i)].Modifier);
				double maxTotalNicSpeed = numNicSockets * maxAllowedNicSpeed;

				int minDiff = (int)Math.Ceiling(i * 0.5);
				GatewayBlueprint g = CreateBlueprint(id.ToLong(), name, numCpuSockets, numMemSockets, numNicSockets, maxTotalCpuSpeed, maxTotalMemory, maxTotalNicSpeed, maxAllowedCpuSpeed, maxAllowedMemSize, maxAllowedNicSpeed, priceBase, minDiff);
				Db.PersistEntity(g);
			}
		}

		private void CreateHighPerformanceBlueprints()
		{
			double priceBase = 4999.0;
			Dictionary<Guid, string> names = new Dictionary<Guid, string>
			{
				{new Guid("993F8CF9-13EF-4ACF-B88A-735199B9A029"), "Standard Super Computer Mk. I"},
				{new Guid("1300A50C-D421-4C26-9ACB-94EB2D35DE72"), "Standard Super Computer Mk. II"},
				{new Guid("3D5CF41F-F2C8-46B3-BA18-D077BFB0BB3D"), "Advanced Super Computer Mk. I"},
				{new Guid("35DF7D8B-1578-4B87-B583-89883A3F4DC1"), "Advanced Super Computer Mk. II"},
				{new Guid("579AC9E1-CDD5-40AF-B460-374A7C96625A"), "Professional Super Computer Mk. I"},
				{new Guid("37097E74-2AA3-40BB-B473-25FCA243BB1D"), "Professional Super Computer Mk. II"},
				{new Guid("AD179B37-EDA1-4726-BC46-5888926623DA"), "High End Super Computer Mk. I"},
				{new Guid("1EA3F376-E416-4C51-8A0D-BB56E434550D"), "High End Super Computer Mk. II"}
			};

			for (int i = 1; i < 9; i++)
			{
				Guid id = names.ElementAt(i - 1).Key;
				string name = names[id];

				int numNicSockets = _hpGetNumNicSockets(i);
				int numCpuSockets = _hpGetNumCpuSockets(i);
				int numMemSockets = _hpGetNumMemSockets(i);

				int maxAllowedMemSize = (int)_createdMemory[_hpGetMemIndex(i)].Modifier;
				int maxTotalMemory = numMemSockets * maxAllowedMemSize;

				double maxAllowedCpuSpeed = Math.Ceiling(_createdCpUs[_hpGetCpuIndex(i)].Modifier);
				double maxTotalCpuSpeed = numCpuSockets * maxAllowedCpuSpeed;

				double maxAllowedNicSpeed = Math.Ceiling(_createdNiCs[_hpGetNicIndex(i)].Modifier);
				double maxTotalNicSpeed = numNicSockets * maxAllowedNicSpeed;

				int minDiff = (int)Math.Ceiling(i * 0.5);
				GatewayBlueprint g = CreateBlueprint(id.ToLong(), name, numCpuSockets, numMemSockets, numNicSockets, maxTotalCpuSpeed, maxTotalMemory, maxTotalNicSpeed, maxAllowedCpuSpeed, maxAllowedMemSize, maxAllowedNicSpeed, priceBase, minDiff);
				Db.PersistEntity(g);
			}
		}

		private GatewayBlueprint CreateBlueprint(
			ulong id, string name,
			int numCpuSockets, int numMemSockets, int numNicSockets,
			double maxTotalCpuSpeed, int maxTotalMemory, double maxTotalBandwidth,
			double maxAllowedCpuSpeed, int maxAllowedMemSize, double maxAllowedBandwidth,
			double basePrice, int mindiff)
		{
			GatewayBlueprint g = (from GatewayBlueprint gwb in Db where gwb.Id == id select gwb).FirstOrDefault() ?? new GatewayBlueprint { Id = id };
			g.Name = name;

			g.NumCpuSlots = numCpuSockets;
			g.NumMemorySlots = numMemSockets;
			g.NumNicSlots = numNicSockets;

			g.MaxCpuSpeed = maxTotalCpuSpeed;
			g.MaxMemorySize = maxTotalMemory;
			g.MaxBandwidth = maxTotalBandwidth;

			g.MaxAllowedMemorySize = maxAllowedMemSize;
			g.MaxAllowedCpuSpeed = maxAllowedCpuSpeed;
			g.MaxAllowedBandwidth = maxAllowedBandwidth;
			g.MinimumMarketDifficulty = mindiff;
			g.BasePrice = (int)Math.Ceiling(basePrice * ((maxAllowedBandwidth + maxAllowedCpuSpeed + maxAllowedMemSize) / 3));
			return g;
		}
	}
}